package martingale

import (
	"database/sql"
	"errors"
	"fmt"
	"math"
	"strings"
	"sync"
	"time"

	"github.com/google/uuid"
)

type ActionType string

const (
	ActionOpen        ActionType = "open"
	ActionPlaceLimit  ActionType = "place_limit"
	ActionCloseAll    ActionType = "close_all"
	ActionUpdateStop  ActionType = "update_stop"
	ActionUpdateTake  ActionType = "update_take"
	ActionCancelOrder ActionType = "cancel_order"
)

type Action struct {
	PlanID          string
	StepIndex       int
	Type            ActionType
	Symbol          string
	Side            string
	PositionSizeUSD float64
	Leverage        int
	Price           float64 // 限价单价格
	StopLoss        float64
	TakeProfit      float64
	NewStopLoss     float64
	NewTakeProfit   float64
	OrderID         int64 // 用于取消订单
	Reason          string
}

type Step struct {
	Index        int     `json:"index"`
	SizeUSD      float64 `json:"size_usd"`
	TriggerPrice float64 `json:"trigger_price"`
	Filled       bool    `json:"filled"`
	Pending      bool    `json:"pending"`
	OrderID      int64   `json:"order_id"` // 挂单ID
}

type Plan struct {
	ID                string    `json:"id"`
	Config            Config    `json:"config"`
	Symbol            string    `json:"symbol"`
	Side              string    `json:"side"`
	Steps             []*Step   `json:"steps"`
	TotalQty          float64   `json:"total_qty"`
	TotalNotional     float64   `json:"total_notional"`
	AvgEntry          float64   `json:"avg_entry"`
	CurrentStopLoss   float64   `json:"current_stop_loss"`
	CurrentTakeProfit float64   `json:"current_take_profit"`
	Closing           bool      `json:"closing"`
	Completed         bool      `json:"completed"`
	CreatedAt         time.Time `json:"created_at"`
	RestartAt         time.Time `json:"restart_at"` // 下一次重启时间
}

type Manager struct {
	mu      sync.Mutex
	plans   map[string]*Plan
	storage *Storage
}

var ErrPlanNotFound = errors.New("martingale plan not found")

func NewManager(db *sql.DB) *Manager {
	return &Manager{
		plans:   make(map[string]*Plan),
		storage: NewStorage(db),
	}
}

func (m *Manager) LoadPlans() error {
	plans, err := m.storage.LoadAll()
	if err != nil {
		return err
	}
	m.mu.Lock()
	defer m.mu.Unlock()
	for _, p := range plans {
		m.plans[p.ID] = p
	}
	return nil
}

// CreatePlan 创建计划但不注册（用于先执行首单，成功后再注册）
func (m *Manager) CreatePlan(cfg Config, currentPrice float64, defaultLeverage int) (*Plan, []Action, error) {
	if err := cfg.Normalize(defaultLeverage); err != nil {
		return nil, nil, err
	}
	if currentPrice <= 0 {
		return nil, nil, fmt.Errorf("invalid current price for %s", cfg.Symbol)
	}

	plan := &Plan{
		ID:        uuid.NewString(),
		Config:    cfg,
		Symbol:    cfg.Symbol,
		Side:      cfg.Side,
		Steps:     []*Step{},
		CreatedAt: time.Now(),
	}

	spacing := cfg.GridSpacingPct / 100
	remaining := cfg.MaxTotalUSD
	capped := remaining > 0
	size := cfg.BaseOrderUSD

	for i := 0; i < cfg.MaxSteps; i++ {
		if capped && remaining <= 0 {
			break
		}
		stepSize := size
		if capped && stepSize > remaining {
			stepSize = remaining
		}
		stepPrice := calcStepPrice(currentPrice, spacing, i, cfg.Side)
		plan.Steps = append(plan.Steps, &Step{
			Index:        i,
			SizeUSD:      stepSize,
			TriggerPrice: stepPrice,
		})
		if capped {
			remaining -= stepSize
		}
		size *= cfg.Multiplier
	}
	if len(plan.Steps) == 0 {
		return nil, nil, fmt.Errorf("no valid steps generated")
	}

	base := plan.Steps[0]
	base.Pending = true

	// Step 0 是市价单
	action0 := plan.buildOpenAction(base, currentPrice, "启动马丁格尔计划")
	actions := []Action{action0}

	// 如果有 Step 1，立即生成限价单
	if len(plan.Steps) > 1 {
		step1 := plan.Steps[1]
		step1.Pending = true
		action1 := plan.buildLimitAction(step1, "预挂第2步限价单")
		actions = append(actions, action1)
	}

	return plan, actions, nil
}

// UpdateStepPrice 更新计划中未成交步骤的触发价格
func (m *Manager) UpdateStepPrice(planID string, stepIndex int, newPrice float64) ([]Action, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	plan, ok := m.plans[planID]
	if !ok {
		return nil, ErrPlanNotFound
	}

	if stepIndex < 0 || stepIndex >= len(plan.Steps) {
		return nil, fmt.Errorf("invalid step index %d", stepIndex)
	}

	step := plan.Steps[stepIndex]
	if step.Filled {
		return nil, fmt.Errorf("step %d already filled, cannot update", stepIndex)
	}

	// 校验新价格是否触发止损（防止重合或穿透）
	if plan.CurrentStopLoss > 0 {
		// 最小安全距离（0.1%）
		safeBuffer := 0.001

		if plan.Side == "long" {
			// 做多：补仓价必须高于止损价
			minAllowed := plan.CurrentStopLoss * (1 + safeBuffer)
			if newPrice <= minAllowed {
				return nil, fmt.Errorf("invalid update: new_price %.4f is too close to stop_loss %.4f (min allowed: %.4f)", newPrice, plan.CurrentStopLoss, minAllowed)
			}
		} else {
			// 做空：补仓价必须低于止损价
			maxAllowed := plan.CurrentStopLoss * (1 - safeBuffer)
			if newPrice >= maxAllowed {
				return nil, fmt.Errorf("invalid update: new_price %.4f is too close to stop_loss %.4f (max allowed: %.4f)", newPrice, plan.CurrentStopLoss, maxAllowed)
			}
		}
	}

	// 更新价格
	oldPrice := step.TriggerPrice
	step.TriggerPrice = newPrice

	var actions []Action

	// 如果该步骤已有挂单，需要先取消旧单，再挂新单
	if step.OrderID > 0 {
		// 1. 先取消旧订单
		actions = append(actions, Action{
			PlanID:    plan.ID,
			StepIndex: stepIndex,
			Type:      ActionCancelOrder,
			Symbol:    plan.Symbol,
			Side:      plan.Side,
			OrderID:   step.OrderID,
			Reason:    fmt.Sprintf("更新步骤%d价格: %.4f -> %.4f (取消旧单)", stepIndex, oldPrice, newPrice),
		})
		// 重置 OrderID，新订单挂上后会再次设置
		step.OrderID = 0

		// 2. 立即生成新的限价单 action（不等待 TickAll）
		step.Pending = true
		actions = append(actions, plan.buildLimitAction(step, fmt.Sprintf("更新步骤%d价格: %.4f -> %.4f (挂新单)", stepIndex, oldPrice, newPrice)))
	}

	// 保存更改
	if err := m.storage.Save(plan); err != nil {
		return nil, fmt.Errorf("failed to save plan: %w", err)
	}

	return actions, nil
}

// RegisterPlan 注册并保存计划
func (m *Manager) RegisterPlan(plan *Plan) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	m.plans[plan.ID] = plan
	return m.storage.Save(plan)
}

func (m *Manager) DeletePlan(planID string) error {
	m.mu.Lock()
	defer m.mu.Unlock()
	if _, ok := m.plans[planID]; !ok {
		return ErrPlanNotFound
	}
	delete(m.plans, planID)
	return m.storage.Delete(planID)
}

// Start (Deprecated) use CreatePlan + RegisterPlan instead
func (m *Manager) Start(cfg Config, currentPrice float64, defaultLeverage int) (*Plan, []Action, error) {
	plan, actions, err := m.CreatePlan(cfg, currentPrice, defaultLeverage)
	if err != nil {
		return nil, nil, err
	}
	if err := m.RegisterPlan(plan); err != nil {
		return nil, nil, err
	}
	return plan, actions, nil
}

func (m *Manager) NotifyFill(planID string, stepIndex int, fillPrice float64, qty float64) ([]Action, error) {
	m.mu.Lock()
	defer m.mu.Unlock()
	plan, ok := m.plans[planID]
	if !ok {
		return nil, fmt.Errorf("plan %s not found", planID)
	}
	if stepIndex < 0 || stepIndex >= len(plan.Steps) {
		return nil, fmt.Errorf("invalid step index %d", stepIndex)
	}
	step := plan.Steps[stepIndex]
	step.Pending = false
	if step.Filled {
		return nil, nil
	}
	step.Filled = true
	if qty <= 0 {
		return nil, nil
	}
	notional := fillPrice * qty
	plan.TotalQty += qty
	plan.TotalNotional += notional
	if plan.TotalQty > 0 {
		plan.AvgEntry = plan.TotalNotional / plan.TotalQty
		plan.updateTargets()
	}

	actions := plan.buildUpdateActions()

	// 触发下一步的限价单
	nextIndex := stepIndex + 1
	if nextIndex < len(plan.Steps) {
		nextStep := plan.Steps[nextIndex]
		if !nextStep.Filled && !nextStep.Pending {
			nextStep.Pending = true
			actions = append(actions, plan.buildLimitAction(nextStep, fmt.Sprintf("预挂第%d步限价单", nextIndex+1)))
		}
	}

	m.storage.Save(plan)
	return actions, nil
}

func (m *Manager) NotifyOrderPlaced(planID string, stepIndex int, orderID int64) {
	m.mu.Lock()
	defer m.mu.Unlock()
	if plan, ok := m.plans[planID]; ok {
		if stepIndex >= 0 && stepIndex < len(plan.Steps) {
			plan.Steps[stepIndex].OrderID = orderID
			m.storage.Save(plan)
		}
	}
}

// GetPendingSteps 获取所有已挂单但未成交的步骤（用于轮询检查状态）
func (m *Manager) GetPendingSteps() []struct {
	PlanID    string
	StepIndex int
	Symbol    string
	OrderID   int64
} {
	m.mu.Lock()
	defer m.mu.Unlock()
	var pending []struct {
		PlanID    string
		StepIndex int
		Symbol    string
		OrderID   int64
	}
	for _, plan := range m.plans {
		if plan.Completed {
			continue
		}
		for _, step := range plan.Steps {
			if step.Pending && step.OrderID > 0 && !step.Filled {
				pending = append(pending, struct {
					PlanID    string
					StepIndex int
					Symbol    string
					OrderID   int64
				}{
					PlanID:    plan.ID,
					StepIndex: step.Index,
					Symbol:    plan.Symbol,
					OrderID:   step.OrderID,
				})
			}
		}
	}
	return pending
}

func (m *Manager) NotifyActionFailure(planID string, stepIndex int) {
	m.mu.Lock()
	defer m.mu.Unlock()
	if plan, ok := m.plans[planID]; ok {
		if stepIndex >= 0 && stepIndex < len(plan.Steps) {
			plan.Steps[stepIndex].Pending = false
			m.storage.Save(plan)
		}
	}
}

func (m *Manager) NotifyClosed(planID string) {
	m.mu.Lock()
	defer m.mu.Unlock()
	if plan, ok := m.plans[planID]; ok {
		plan.TotalQty = 0
		plan.TotalNotional = 0
		plan.AvgEntry = 0
		plan.Closing = false
		// 不再支持循环重启，直接标记为完成，让 AI 决定是否新开计划
		plan.Completed = true
		m.storage.Save(plan)
	}
}

func (m *Manager) TickAll(priceMap map[string]float64) []Action {
	m.mu.Lock()
	defer m.mu.Unlock()
	var actions []Action
	for _, plan := range m.plans {
		if plan.Completed {
			continue
		}

		price, ok := priceMap[plan.Symbol]
		if !ok || price <= 0 {
			continue
		}

		// ------------------------------------------------------
		// 补单逻辑：检查是否有应该挂单但未挂单的步骤 (Fix: 防止挂单失败导致策略中断)
		// ------------------------------------------------------
		var nextStep *Step
		for _, step := range plan.Steps {
			if !step.Filled {
				nextStep = step
				break
			}
		}

		// 只有当上一步已经 Filled 时，才挂这一步
		// Step 0 是市价单，不在这里补挂（由 CreatePlan 或 Restart 触发）
		if nextStep != nil && !nextStep.Pending && nextStep.Index > 0 {
			prevStep := plan.Steps[nextStep.Index-1]
			if prevStep.Filled {
				nextStep.Pending = true
				action := plan.buildLimitAction(nextStep, fmt.Sprintf("补挂第%d步限价单(检测到漏单)", nextStep.Index+1))
				actions = append(actions, action)
				m.storage.Save(plan) // 保存 Pending 状态
				// 注意：这里不 continue，允许后续继续检查 TP/SL
			}
		}
		// ------------------------------------------------------

		actions = append(actions, plan.evaluatePrice(price)...)
	}
	return actions
}

func (m *Manager) GetActiveSymbols() []string {
	m.mu.Lock()
	defer m.mu.Unlock()
	seen := make(map[string]bool)
	var symbols []string
	for _, plan := range m.plans {
		if !plan.Completed && !seen[plan.Symbol] {
			seen[plan.Symbol] = true
			symbols = append(symbols, plan.Symbol)
		}
	}
	return symbols
}

// GetPlans 获取所有计划的状态
func (m *Manager) GetPlans() []*Plan {
	m.mu.Lock()
	defer m.mu.Unlock()
	var plans []*Plan
	for _, plan := range m.plans {
		plans = append(plans, plan)
	}
	return plans
}

// GetActivePlan 返回给定 symbol/side 的活跃马丁格尔计划
func (m *Manager) GetActivePlan(symbol, side string) *Plan {
	m.mu.Lock()
	defer m.mu.Unlock()

	symbol = strings.ToUpper(symbol)
	side = strings.ToLower(side)

	for _, plan := range m.plans {
		if plan.Completed {
			continue
		}
		if strings.EqualFold(plan.Symbol, symbol) && strings.EqualFold(plan.Side, side) {
			return plan
		}
	}
	return nil
}

// IsManagedPosition 返回给定 symbol/side 是否由活跃的马丁格尔计划管理（且存在持仓）
func (m *Manager) IsManagedPosition(symbol, side string) bool {
	m.mu.Lock()
	defer m.mu.Unlock()

	symbol = strings.ToUpper(symbol)
	side = strings.ToLower(side)

	for _, plan := range m.plans {
		if plan.Completed {
			continue
		}
		if strings.EqualFold(plan.Symbol, symbol) && strings.EqualFold(plan.Side, side) && plan.TotalQty > 0 {
			return true
		}
	}

	return false
}

func (plan *Plan) evaluatePrice(price float64) []Action {
	var actions []Action
	// Limit orders are pre-placed, so we don't need to trigger them here.
	// We only check for TP/SL.

	if plan.TotalQty > 0 && !plan.Closing {
		if plan.shouldTakeProfit(price) {
			plan.Closing = true
			actions = append(actions, plan.buildCloseAction("马丁格尔止盈"))
		} else if plan.shouldStopLoss(price) {
			plan.Closing = true
			actions = append(actions, plan.buildCloseAction("马丁格尔止损"))
		}
	}

	return actions
}

// shouldTriggerStep (Unused but kept for potential future logic)
// func (plan *Plan) shouldTriggerStep(step *Step, price float64) bool {
// 	if plan.Side == "long" {
// 		return price <= step.TriggerPrice
// 	}
// 	return price >= step.TriggerPrice
// }

func (plan *Plan) shouldTakeProfit(price float64) bool {
	if plan.CurrentTakeProfit == 0 {
		return false
	}
	if plan.Side == "long" {
		return price >= plan.CurrentTakeProfit
	}
	return price <= plan.CurrentTakeProfit
}

func (plan *Plan) shouldStopLoss(price float64) bool {
	if plan.CurrentStopLoss == 0 {
		return false
	}
	if plan.Side == "long" {
		return price <= plan.CurrentStopLoss
	}
	return price >= plan.CurrentStopLoss
}

func (plan *Plan) buildOpenAction(step *Step, refPrice float64, reason string) Action {
	stop := calcStopPrice(plan.Side, refPrice, plan.Config.StopLossPct)
	take := calcTakeProfitPrice(plan.Side, refPrice, plan.Config.TakeProfitPct)
	action := Action{
		PlanID:          plan.ID,
		StepIndex:       step.Index,
		Type:            ActionOpen,
		Symbol:          plan.Symbol,
		Side:            plan.Side,
		PositionSizeUSD: step.SizeUSD,
		Leverage:        plan.Config.Leverage,
		StopLoss:        stop,
		TakeProfit:      take,
		Reason:          reason,
	}
	return action
}

func (plan *Plan) buildCloseAction(reason string) Action {
	return Action{
		PlanID:    plan.ID,
		StepIndex: -1,
		Type:      ActionCloseAll,
		Symbol:    plan.Symbol,
		Side:      plan.Side,
		Reason:    reason,
	}
}

func (plan *Plan) buildUpdateActions() []Action {
	var actions []Action
	if plan.CurrentStopLoss > 0 {
		actions = append(actions, Action{
			PlanID:      plan.ID,
			StepIndex:   -1,
			Type:        ActionUpdateStop,
			Symbol:      plan.Symbol,
			Side:        plan.Side,
			NewStopLoss: plan.CurrentStopLoss,
			Reason:      "马丁格尔动态调整止损",
		})
	}
	if plan.CurrentTakeProfit > 0 {
		actions = append(actions, Action{
			PlanID:        plan.ID,
			StepIndex:     -1,
			Type:          ActionUpdateTake,
			Symbol:        plan.Symbol,
			Side:          plan.Side,
			NewTakeProfit: plan.CurrentTakeProfit,
			Reason:        "马丁格尔动态调整止盈",
		})
	}
	return actions
}

func (plan *Plan) buildLimitAction(step *Step, reason string) Action {
	return Action{
		PlanID:          plan.ID,
		StepIndex:       step.Index,
		Type:            ActionPlaceLimit,
		Symbol:          plan.Symbol,
		Side:            plan.Side,
		PositionSizeUSD: step.SizeUSD,
		Leverage:        plan.Config.Leverage,
		Price:           step.TriggerPrice,
		Reason:          reason,
	}
}

func (plan *Plan) updateTargets() {
	if plan.TotalQty <= 0 {
		plan.CurrentStopLoss = 0
		plan.CurrentTakeProfit = 0
		return
	}
	plan.CurrentStopLoss = calcStopPrice(plan.Side, plan.AvgEntry, plan.Config.StopLossPct)
	plan.CurrentTakeProfit = calcTakeProfitPrice(plan.Side, plan.AvgEntry, plan.Config.TakeProfitPct)
}

func calcStopPrice(side string, price float64, pct float64) float64 {
	factor := pct / 100
	if side == "long" {
		return price * (1 - factor)
	}
	return price * (1 + factor)
}

func calcTakeProfitPrice(side string, price float64, pct float64) float64 {
	factor := pct / 100
	if side == "long" {
		return price * (1 + factor)
	}
	return price * (1 - factor)
}

func calcStepPrice(basePrice float64, spacing float64, index int, side string) float64 {
	if index == 0 {
		return basePrice
	}
	if side == "long" {
		price := basePrice * math.Pow(1-spacing, float64(index))
		if price < 0.0001 {
			price = 0.0001
		}
		return price
	}
	price := basePrice * math.Pow(1+spacing, float64(index))
	if price < 0.0001 {
		price = 0.0001
	}
	return price
}
